import gc

from imports.binaryboard import *
from imports.cell import *

class TestBoard:
    def __init__(self, complex_board):
        self.cell = []
        for i in complex_board.cell:
            self.cell.append(i.value)
        self.solution = set(complex_board.solution)
        self.rows = complex_board.rows
        self.columns = complex_board.columns
    def is_valid(self):
        candidates = set(range(len(self.cell))).difference(self.solution)
        for i in candidates:
            if self.has_same_in_row(i) or self.has_same_in_column(i):
                if self.test(i):
                    return False
        return True
    def __str__(self):
        if len(self.cell) == 0:
            return ""
        string = ""
        for i in range(self.rows):
            for j in range(self.columns):
                string += str(self.cell[i*self.columns+j]) + ' '
            string = string[:-1] + '\n'
        return string[:-1]
        
    def has_same_in_column(self, index):
        i = index%self.columns
        ct = 0
        while i < len(self.cell):
            if self.cell[i] == self.cell[index]:
                ct += 1
                if ct == 2:
                    return True
            i += self.columns
        return False
    def has_same_in_row(self, index):
        row = int(index/self.columns)
        i = row*self.columns
        ct = 0
        for j in range(self.columns):
            if self.cell[i+j] == self.cell[index]:
                ct += 1
                if ct == 2:
                    return True
        return False
        
    def test(self, index):
        self.bin = BinaryBoard(self.rows, self.columns)
        
        if self.mark(index):
            while True:
                self.has_changed = False
                if not self.sweep():
                    return False
                if self.has_changed == False:
                    return True
        else:
            return False
        
    def mark(self, index):
        if not self.bin.check_adjacent(index):
            return False
        if not self.bin.check_diagonal(index):
            return False
        self.bin.cell[index] = 1
        
        for i in {2,4,6,8}:
            target = self.bin.get_position(index, i)
            if target >= 0:
                if not self.protect(target):
                    return False
        return True
        
    def protect(self, index):
        if self.bin.cell[index] == 1:
            return False
        elif self.bin.cell[index] == 2:
            return True
        elif self.bin.cell[index] == 0:
            self.bin.cell[index] = 2
            if not self.mark_row(index):
                return False
            if not self.mark_column(index):
                return False
        return True
        
    def sweep(self):
        for i in range(len(self.cell)):
            if self.bin.cell[i] == 0:
                if not self.bin.check_diagonal(i):
                    if not self.protect(i):
                        return False
                    self.has_changed = True
        return True
        
    def mark_row(self, index):
        row = int(index/self.columns)
        i = row*self.columns
        for j in range(self.columns):
            if self.cell[i+j] == self.cell[index]:
                if i+j != index:
                    if self.bin.cell[i+j] == 0:
                        if not self.mark(i+j):
                            return False
                    elif self.bin.cell[i+j] == 2:
                        return False
        return True
    def mark_column(self, index):
        i = index%self.columns
        while i < len(self.cell):
            if self.cell[i] == self.cell[index]:
                if i != index:
                    if self.bin.cell[i] == 0:
                        if not self.mark(i):
                            return False
                    elif self.bin.cell[i] == 2:
                        return False
            i += self.columns
        return True